home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pine / os.c < prev    next >
C/C++ Source or Header  |  1996-04-16  |  66KB  |  2,485 lines

  1. /*----------------------------------------------------------------------
  2.  
  3.             T H E    P I N E    M A I L   S Y S T E M
  4.  
  5.    Laurence Lundblade and Mike Seibel
  6.    Networks and Distributed Computing
  7.    Computing and Communications
  8.    University of Washington
  9.    Administration Builiding, AG-44
  10.    Seattle, Washington, 98195, USA
  11.    Internet: lgl@CAC.Washington.EDU
  12.              mikes@CAC.Washington.EDU
  13.  
  14.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  15.  
  16.  
  17.    Pine and Pico are registered trademarks of the University of Washington.
  18.    No commercial use of these trademarks may be made without prior written
  19.    permission of the University of Washington.
  20.  
  21.    Pine, Pico, and Pilot software and its included text are Copyright
  22.    1989-1996 by the University of Washington.
  23.  
  24.    The full text of our legal notices is contained in the file called
  25.    CPYRIGHT, included with this distribution.
  26.  
  27.  
  28.    Pine is in part based on The Elm Mail System:
  29.     ***********************************************************************
  30.     *  The Elm Mail System  -  Revision: 2.13                             *
  31.     *                                                                     *
  32.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  33.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  34.     ***********************************************************************
  35.  
  36.  
  37.   ----------------------------------------------------------------------*/
  38.  
  39. /*======================================================================
  40.  
  41.  This contains most of Pine's interface to the local operating system
  42. and hardware.  Hopefully this file, os-xxx.h and makefile.xxx are the
  43. only ones that have to be modified for most ports.  Signals.c, ttyin.c,
  44. and ttyout.c also have some dependencies.  See the doc/tech-notes for
  45. notes on porting Pine to other platforms.  Here is a list of the functions
  46. required for an implementation:
  47.  
  48.  
  49.   File System Access
  50.      can_access          -- See if a file can be accessed
  51.      name_file_size      -- Return the number of bytes in the file (by name)
  52.      fp_file_size        -- Return the number of bytes in the file (by FILE *)
  53.      name_file_mtime     -- Return the mtime of a file (by name)
  54.      fp_file_mtime       -- Return the mtime of a file (by FILE *)
  55.      file_attrib_copy    -- Copy attributes of one file to another.
  56.      is_writable_dir     -- Check to see if directory exists and is writable
  57.      create_mail_dir     -- Make a directory
  58.      rename_file         -- change name of a file
  59.      build_path          -- Put together a file system path
  60.      last_cmpnt          -- Returns pointer to last component of path
  61.      expand_foldername   -- Expand a folder name to full path
  62.      fnexpand            -- Do filename exansion for csh style "~"
  63.      filter_filename     -- Make sure file name hasn't got weird chars
  64.      cntxt_allowed       -- Check whether a pathname is allowed for read/write
  65.      disk_quota          -- Check the user's disk quota
  66.      read_file           -- Read whole file into memory (for small files)
  67.      create_tmpfile      -- Just like ANSI C tmpfile function
  68.      temp_nam            -- Almost like common tempnam function
  69.      fget_pos,fset_pos   -- Just like ANSI C fgetpos, fsetpos functions
  70.  
  71.   Abort
  72.      coredump            -- Abort running Pine dumping core if possible
  73.  
  74.   System Name and Domain
  75.      hostname            -- Figure out the system's host name, only
  76.                               used internally in this file.
  77.      getdomainnames      -- Figure out the system's domain name
  78.      canonical_name      -- Returns canonical form of host name
  79.  
  80.   Job Control
  81.      have_job_control    -- Returns 1 if job control exists
  82.      stop_process        -- What to do to stop process when it's time to stop
  83.                   (only used if have_job_control returns 1)
  84.  
  85.   System Error Messages (in case given one is a problem)
  86.      error_description   -- Returns string describing error
  87.  
  88.   System Password and Accounts
  89.      gcos_name           -- Parses full name from system, only used
  90.                   locally in this file so if you don't use it you
  91.                   don't need it
  92.      get_user_info       -- Finds in login name, full name, and homedir
  93.      local_name_lookup   -- Get full name of user on system
  94.      change_passwd       -- Calls system password changer
  95.  
  96.   MIME utilities
  97.      mime_can_display    -- Can we display this type/subtype?
  98.      exec_mailcap_cmd    -- Run the mailcap command to view a type/subtype.
  99.      exec_mailcap_test_cmd -- Run mailcap test= test command.
  100.  
  101.   Other stuff
  102.      srandom             -- Dummy srandom if you don't have this function
  103.      init_debug
  104.      do_debug
  105.      save_debug_on_crash
  106.  
  107.   ====*/
  108.  
  109.  
  110. #include "headers.h"
  111.  
  112.  
  113.  
  114. /*----------------------------------------------------------------------
  115.        Check if we can access a file in a given way
  116.  
  117.    Args: file      -- The file to check
  118.          mode      -- The mode ala the access() system call, see ACCESS_EXISTS
  119.                       and friends in pine.h.
  120.  
  121.  Result: returns 0 if the user can access the file according to the mode,
  122.          -1 if he can't (and errno is set).
  123.  ----*/
  124. int
  125. can_access(file, mode)
  126.     char *file;
  127.     int   mode;
  128. {
  129.     return(access(file, mode));
  130. }
  131.  
  132. /*----------------------------------------------------------------------
  133.       Return the number of bytes in given file
  134.  
  135.     Args: file -- file name
  136.  
  137.   Result: the number of bytes in the file is returned or
  138.           -1 on error, in which case errno is valid
  139.  ----*/
  140. long
  141. name_file_size(file)
  142.     char *file;
  143. {
  144.     struct stat buffer;
  145.  
  146.     if(stat(file, &buffer) != 0)
  147.       return(-1L);
  148.  
  149.     return((long)buffer.st_size);
  150. }
  151.  
  152.  
  153. /*----------------------------------------------------------------------
  154.       Return the number of bytes in given file
  155.  
  156.     Args: fp -- FILE * for open file
  157.  
  158.   Result: the number of bytes in the file is returned or
  159.           -1 on error, in which case errno is valid
  160.  ----*/
  161. long
  162. fp_file_size(fp)
  163.     FILE *fp;
  164. {
  165.     struct stat buffer;
  166.  
  167.     if(fstat(fileno(fp), &buffer) != 0)
  168.       return(-1L);
  169.  
  170.     return((long)buffer.st_size);
  171. }
  172.  
  173.  
  174. /*----------------------------------------------------------------------
  175.       Return the modification time of given file
  176.  
  177.     Args: file -- file name
  178.  
  179.   Result: the time of last modification (mtime) of the file is returned or
  180.           -1 on error, in which case errno is valid
  181.  ----*/
  182. time_t
  183. name_file_mtime(file)
  184.     char *file;
  185. {
  186.     struct stat buffer;
  187.  
  188.     if(stat(file, &buffer) != 0)
  189.       return((time_t)(-1));
  190.  
  191.     return(buffer.st_mtime);
  192. }
  193.  
  194.  
  195. /*----------------------------------------------------------------------
  196.       Return the modification time of given file
  197.  
  198.     Args: fp -- FILE * for open file
  199.  
  200.   Result: the time of last modification (mtime) of the file is returned or
  201.           -1 on error, in which case errno is valid
  202.  ----*/
  203. time_t
  204. fp_file_mtime(fp)
  205.     FILE *fp;
  206. {
  207.     struct stat buffer;
  208.  
  209.     if(fstat(fileno(fp), &buffer) != 0)
  210.       return((time_t)(-1));
  211.  
  212.     return(buffer.st_mtime);
  213. }
  214.  
  215.  
  216. /*----------------------------------------------------------------------
  217.       Copy the mode, owner, and group of sourcefile to targetfile.
  218.  
  219.     Args: targetfile -- 
  220.       sourcefile --
  221.     
  222.     We don't bother keeping track of success or failure because we don't care.
  223.  ----*/
  224. void
  225. file_attrib_copy(targetfile, sourcefile)
  226.     char *targetfile;
  227.     char *sourcefile;
  228. {
  229.     struct stat buffer;
  230.  
  231.     if(stat(sourcefile, &buffer) == 0){
  232.     chmod(targetfile, buffer.st_mode);
  233. #if !defined(DOS) && !defined(OS2)
  234.     chown(targetfile, buffer.st_uid, buffer.st_gid);
  235. #endif
  236.     }
  237. }
  238.  
  239.  
  240.  
  241. /*----------------------------------------------------------------------
  242.       Check to see if a directory exists and is writable by us
  243.  
  244.    Args: dir -- directory name
  245.  
  246.  Result:       returns 0 if it exists and is writable
  247.                        1 if it is a directory, but is not writable
  248.                        2 if it is not a directory
  249.                        3 it doesn't exist.
  250.   ----*/
  251. is_writable_dir(dir)
  252.     char *dir;
  253. {
  254.     struct stat sb;
  255.  
  256.     if(stat(dir, &sb) < 0)
  257.       /*--- It doesn't exist ---*/
  258.       return(3);
  259.  
  260.     if(!(sb.st_mode & S_IFDIR))
  261.       /*---- it's not a directory ---*/
  262.       return(2);
  263.  
  264.     if(can_access(dir, 07))
  265.       return(1);
  266.     else
  267.       return(0);
  268. }
  269.  
  270.  
  271.  
  272. /*----------------------------------------------------------------------
  273.       Create the mail subdirectory.
  274.  
  275.   Args: dir -- Name of the directory to create
  276.  
  277.  Result: Directory is created.  Returns 0 on success, else -1 on error
  278.      and errno is valid.
  279.   ----*/
  280. create_mail_dir(dir)
  281.     char *dir;
  282. {
  283.     if(mkdir(dir, 0700) < 0)
  284.       return(-1);
  285.  
  286.     (void)chmod(dir, 0700);
  287.     /* Some systems need this, on others we don't care if it fails */
  288.     (void)chown(dir, getuid(), getgid());
  289.     return(0);
  290. }
  291.  
  292.  
  293.  
  294. /*----------------------------------------------------------------------
  295.       Rename a file
  296.  
  297.   Args: tmpfname -- Old name of file
  298.         fname    -- New name of file
  299.  
  300.  Result: File is renamed.  Returns 0 on success, else -1 on error
  301.      and errno is valid.
  302.   ----*/
  303. rename_file(tmpfname, fname)
  304.     char *tmpfname, *fname;
  305. {
  306.     return(rename(tmpfname, fname));
  307. }
  308.  
  309.  
  310.  
  311. /*----------------------------------------------------------------------
  312.       Paste together two pieces of a file name path
  313.  
  314.   Args: pathbuf      -- Put the result here
  315.         first_part   -- of path name
  316.         second_part  -- of path name
  317.  
  318.  Result: New path is in pathbuf.  No check is made for overflow.  Note that
  319.      we don't have to check for /'s at end of first_part and beginning
  320.      of second_part since multiple slashes are ok.
  321.  
  322. BUGS:  This is a first stab at dealing with fs naming dependencies, and others 
  323. still exist.
  324.   ----*/
  325. void
  326. build_path(pathbuf, first_part, second_part)
  327.     char *pathbuf, *first_part, *second_part;
  328. {
  329.     if(!first_part)
  330.       strcpy(pathbuf, second_part);
  331.     else
  332.       sprintf(pathbuf, "%s%s%s", first_part,
  333.           (*first_part && first_part[strlen(first_part)-1] != '/')
  334.             ? "/" : "",
  335.           second_part);
  336. }
  337.  
  338.  
  339. /*----------------------------------------------------------------------
  340.   Test to see if the given file path is absolute
  341.  
  342.   Args: file -- file path to test
  343.  
  344.  Result: TRUE if absolute, FALSE otw
  345.  
  346.   ----*/
  347. int
  348. is_absolute_path(path)
  349.     char *path;
  350. {
  351.     return(path && (*path == '/' || *path == '~'));
  352. }
  353.  
  354.  
  355.  
  356. /*----------------------------------------------------------------------
  357.       Return pointer to last component of pathname.
  358.  
  359.   Args: filename     -- The pathname.
  360.  
  361.  Result: Returned pointer points to last component in the input argument.
  362.   ----*/
  363. char *
  364. last_cmpnt(filename)
  365.     char *filename;
  366. {
  367.     register char *p = NULL, *q = filename;
  368.  
  369.     while(q = strchr(q, '/'))
  370.       if(*++q)
  371.     p = q;
  372.  
  373.     return(p);
  374. }
  375.  
  376.  
  377.  
  378. /*----------------------------------------------------------------------
  379.       Expand a folder name, taking account of the folders_dir and `~'.
  380.  
  381.   Args: filename -- The name of the file that is the folder
  382.  
  383.  Result: The folder name is expanded in place.  
  384.          Returns 0 and queues status message if unsuccessful.
  385.          Input string is overwritten with expanded name.
  386.          Returns 1 if successful.
  387.  
  388. BUG should limit length to MAXPATH
  389.   ----*/
  390. int
  391. expand_foldername(filename)
  392.     char *filename;
  393. {
  394.     char         temp_filename[MAXPATH+1];
  395.  
  396.     dprint(5, (debugfile, "=== expand_foldername called (%s) ===\n",filename));
  397.  
  398.     /*
  399.      * We used to check for valid filename chars here if "filename"
  400.      * didn't refer to a remote mailbox.  This has been rethought
  401.      */
  402.  
  403.     strcpy(temp_filename, filename);
  404.     if(strucmp(temp_filename, "inbox") == 0) {
  405.         strcpy(filename, ps_global->VAR_INBOX_PATH == NULL ? "inbox" :
  406.                ps_global->VAR_INBOX_PATH);
  407.     } else if(temp_filename[0] == '{') {
  408.         strcpy(filename, temp_filename);
  409.     } else if(ps_global->restricted
  410.             && (strindex("./~", temp_filename[0]) != NULL
  411.             || srchstr(temp_filename,"/../"))){
  412.     q_status_message(SM_ORDER, 0, 3, "Can only open local folders");
  413.     return(0);
  414.     } else if(temp_filename[0] == '*') {
  415.         strcpy(filename, temp_filename);
  416.     } else if(ps_global->VAR_OPER_DIR && srchstr(temp_filename,"..")){
  417.     q_status_message(SM_ORDER, 0, 3,
  418.              "\"..\" not allowed in folder name");
  419.     return(0);
  420.     } else if (temp_filename[0] == '~'){
  421.         if(fnexpand(temp_filename, sizeof(temp_filename)) == NULL) {
  422.             char *p = strindex(temp_filename, '/');
  423.             if(p != NULL)
  424.               *p = '\0';
  425.             q_status_message1(SM_ORDER, 3, 3,
  426.                     "Error expanding folder name: \"%s\" unknown user",
  427.                temp_filename);
  428.             return(0);
  429.         }
  430.         strcpy(filename, temp_filename);
  431.     } else if(temp_filename[0] == '/') {
  432.         strcpy(filename, temp_filename);
  433.     } else if(F_ON(F_USE_CURRENT_DIR, ps_global)){
  434.     strcpy(filename, temp_filename);
  435.     } else if(ps_global->VAR_OPER_DIR){
  436.     build_path(filename, ps_global->VAR_OPER_DIR, temp_filename);
  437.     } else {
  438.     build_path(filename, ps_global->home_dir, temp_filename);
  439.     }
  440.     dprint(5, (debugfile, "returning \"%s\"\n", filename));    
  441.     return(1);
  442. }
  443.  
  444.  
  445.  
  446. struct passwd *getpwnam();
  447.  
  448. /*----------------------------------------------------------------------
  449.        Expand the ~ in a file ala the csh (as home directory)
  450.  
  451.    Args: buf --  The filename to expand (nothing happens unless begins with ~)
  452.          len --  The length of the buffer passed in (expansion is in place)
  453.  
  454.  Result: Expanded string is returned using same storage as passed in.
  455.          If expansion fails, NULL is returned
  456.  ----*/
  457. char *
  458. fnexpand(buf, len)
  459.     char *buf;
  460.     int len;
  461. {
  462.     struct passwd *pw;
  463.     register char *x,*y;
  464.     char name[20];
  465.     
  466.     if(*buf == '~') {
  467.         for(x = buf+1, y = name; *x != '/' && *x != '\0'; *y++ = *x++);
  468.         *y = '\0';
  469.         if(x == buf + 1) 
  470.           pw = getpwuid(getuid());
  471.         else
  472.           pw = getpwnam(name);
  473.         if(pw == NULL)
  474.           return((char *)NULL);
  475.         if(strlen(pw->pw_dir) + strlen(buf) > len) {
  476.           return((char *)NULL);
  477.         }
  478.         rplstr(buf, x - buf, pw->pw_dir);
  479.     }
  480.     return(len ? buf : (char *)NULL);
  481. }
  482.  
  483.  
  484.  
  485. /*----------------------------------------------------------------------
  486.     Filter file names for strange characters
  487.  
  488.    Args:  file  -- the file name to check
  489.  
  490.  Result: Returns NULL if file name is OK
  491.          Returns formatted error message if it is not
  492.   ----*/
  493. char *
  494. filter_filename(file)
  495.     char *file;
  496. {
  497. #ifdef ALLOW_WEIRD
  498.     static char illegal[] = {'\177', '\0'};
  499. #else
  500.     static char illegal[] = {'"', '#', '$', '%', '&', '\'','(', ')','*',
  501.                           ',', ':', ';', '<', '=', '>', '?', '[', ']',
  502.                           '\\', '^', '|', '\177', '\0'};
  503. #endif
  504.     static char error[100];
  505.     char ill_file[MAXPATH+1], *ill_char, *ptr, e2[10];
  506.     int i;
  507.  
  508.     for(ptr = file; *ptr == ' '; ptr++) ; /* leading spaces gone */
  509.  
  510.     while(*ptr && *ptr > ' ' && strindex(illegal, *ptr) == 0)
  511.       ptr++;
  512.  
  513.     if(*ptr != '\0') {
  514.         if(*ptr == ' ') {
  515.             ill_char = "<space>";
  516.         } else if(*ptr == '\n') {
  517.             ill_char = "<newline>";
  518.         } else if(*ptr == '\r') {
  519.             ill_char = "<carriage return>";
  520.         } else if(*ptr == '\t') {
  521.             ill_char = "<tab>";
  522.         } else if(*ptr < ' ') {
  523.             sprintf(e2, "control-%c", *ptr + '@');
  524.             ill_char = e2;
  525.         } else if (*ptr == '\177') {
  526.             ill_char = "<del>";
  527.         } else {
  528.             e2[0] = *ptr;
  529.             e2[1] = '\0';
  530.             ill_char = e2;
  531.         }
  532.         if(ptr != file) {
  533.             strncpy(ill_file, file, ptr - file);
  534.             ill_file[ptr - file] = '\0';
  535.             sprintf(error,
  536.             "Character \"%s\" after \"%s\" not allowed in file name",
  537.             ill_char, ill_file);
  538.         } else {
  539.             sprintf(error,
  540.                     "First character, \"%s\", not allowed in file name",
  541.                     ill_char);
  542.         }
  543.             
  544.         return(error);
  545.     }
  546.  
  547.     if((i=is_writable_dir(file)) == 0 || i == 1){
  548.     sprintf(error, "\"%s\" is a directory", file);
  549.         return(error);
  550.     }
  551.  
  552.     if(ps_global->restricted || ps_global->VAR_OPER_DIR){
  553.     for(ptr = file; *ptr == ' '; ptr++) ;    /* leading spaces gone */
  554.  
  555.     if((ptr[0] == '.' && ptr[1] == '.') || srchstr(ptr, "/../")){
  556.         sprintf(error, "\"..\" not allowed in filename");
  557.         return(error);
  558.     }
  559.     }
  560.  
  561.     return((char *)NULL);
  562. }
  563.  
  564.  
  565. /*----------------------------------------------------------------------
  566.     Check to see if user is allowed to read or write this folder.
  567.  
  568.    Args:  s  -- the name to check
  569.  
  570.  Result: Returns 1 if OK
  571.          Returns 0 and posts an error message if access is denied
  572.   ----*/
  573. int
  574. cntxt_allowed(s)
  575.     char *s;
  576. {
  577.     struct variable *vars = ps_global->vars;
  578.     int retval = 1;
  579.     MAILSTREAM stream; /* fake stream for error message in mm_notify */
  580.  
  581.     if(ps_global->restricted
  582.          && (strindex("./~", s[0]) || srchstr(s, "/../"))){
  583.     stream.mailbox = s;
  584.     mm_notify(&stream, "Restricted mode doesn't allow operation", WARN);
  585.     retval = 0;
  586.     }
  587.     else if(VAR_OPER_DIR
  588.         && s[0] != '{' && !(s[0] == '*' && s[1] == '{')
  589.         && strucmp(s,ps_global->inbox_name) != 0
  590.         && strcmp(s, ps_global->VAR_INBOX_PATH) != 0){
  591.     char *p, *free_this = NULL;
  592.  
  593.     p = s;
  594.     if(strindex(s, '~')){
  595.         p = strindex(s, '~');
  596.         free_this = (char *)fs_get(strlen(p) + 200);
  597.         strcpy(free_this, p);
  598.         fnexpand(free_this, strlen(p)+200);
  599.         p = free_this;
  600.     }
  601.     else if(p[0] != '/'){  /* add home dir to relative paths */
  602.         free_this = p = (char *)fs_get(strlen(s)
  603.                         + strlen(ps_global->home_dir) + 2);
  604.         build_path(p, ps_global->home_dir, s);
  605.     }
  606.     
  607.     if(!in_dir(VAR_OPER_DIR, p)){
  608.         char err[200];
  609.  
  610.         sprintf(err, "Not allowed outside of %s", VAR_OPER_DIR);
  611.         stream.mailbox = p;
  612.         mm_notify(&stream, err, WARN);
  613.         retval = 0;
  614.     }
  615.     else if(srchstr(p, "/../")){  /* check for .. in path */
  616.         stream.mailbox = p;
  617.         mm_notify(&stream, "\"..\" not allowed in name", WARN);
  618.         retval = 0;
  619.     }
  620.  
  621.     if(free_this)
  622.       fs_give((void **)&free_this);
  623.     }
  624.     
  625.     return retval;
  626. }
  627.  
  628.  
  629.  
  630. #if defined(USE_QUOTAS)
  631.  
  632. /*----------------------------------------------------------------------
  633.    This system doesn't have disk quotas.
  634.    Return space left in disk quota on file system which given path is in.
  635.  
  636.     Args: path - Path name of file or directory on file system of concern
  637.           over - pointer to flag that is set if the user is over quota
  638.  
  639.  Returns: If *over = 0, the number of bytes free in disk quota as per
  640.           the soft limit.
  641.       If *over = 1, the number of bytes *over* quota.
  642.           -1 is returned on an error looking up quota
  643.            0 is returned if there is no quota
  644.  
  645. BUG:  If there's more than 2.1Gb free this function will break
  646.   ----*/
  647. long
  648. disk_quota(path, over)
  649.     char *path;
  650.     int  *over;
  651. {
  652.     return(0L);
  653. }
  654. #endif /* USE_QUOTAS */
  655.  
  656.  
  657.  
  658. /*----------------------------------------------------------------------
  659.     Read whole file into memory
  660.  
  661.   Args: filename -- path name of file to read
  662.  
  663.   Result: Returns pointer to malloced memory with the contents of the file
  664.           or NULL
  665.  
  666. This won't work very well if the file has NULLs in it and is mostly
  667. intended for fairly small text files.
  668.  ----*/
  669. char *
  670. read_file(filename)
  671.     char *filename;
  672. {
  673.     int         fd;
  674.     struct stat statbuf;
  675.     char       *buf;
  676.     int         nb;
  677.  
  678.     fd = open(filename, O_RDONLY);
  679.     if(fd < 0)
  680.       return((char *)NULL);
  681.  
  682.     fstat(fd, &statbuf);
  683.  
  684.     buf = fs_get((size_t)statbuf.st_size + 1);
  685.  
  686.     /*
  687.      * On some systems might have to loop here, if one read isn't guaranteed
  688.      * to get the whole thing.
  689.      */
  690.     if((nb = read(fd, buf, (int)statbuf.st_size)) < 0)
  691.       fs_give((void **)&buf);        /* NULL's buf */
  692.     else
  693.       buf[nb] = '\0';
  694.  
  695.     close(fd);
  696.     return(buf);
  697. }
  698.  
  699.  
  700.  
  701. /*----------------------------------------------------------------------
  702.    Create a temporary file, the name of which we don't care about 
  703. and that goes away when it is closed.  Just like ANSI C tmpfile.
  704.   ----*/
  705. FILE  *
  706. create_tmpfile()
  707. {
  708.     return(tmpfile());
  709. }
  710.  
  711.  
  712.  
  713. /*
  714.  * This routine is derived from BSD4.3 code,
  715.  * Copyright (c) 1988 Regents of the University of California.
  716.  * All rights reserved.
  717.  */
  718. #if defined(LIBC_SCCS) && !defined(lint)
  719. static char sccsid[] = "@(#)tmpnam.c    4.5 (Berkeley) 6/27/88";
  720. #endif /* LIBC_SCCS and not lint */
  721. /*----------------------------------------------------------------------
  722.       Return a unique file name in a given directory.  This is not quite
  723.       the same as the usual tempnam() function, though it is very similar.
  724.       We want it to use the TMP environment variable only if dir is NULL,
  725.       instead of using TMP regardless if it is set.
  726.  
  727.   Args: dir      -- The directory to create the name in
  728.         prefix   -- Prefix of the name
  729.  
  730.  Result: Malloc'd string equal to new name is returned.  It must be free'd
  731.      by the caller.  Returns the string on success and NULL on failure.
  732.   ----*/
  733. char *
  734. temp_nam(dir, prefix)
  735.     char *dir, *prefix;
  736. {
  737.     struct stat buf;
  738.     char *f, *name;
  739.     char *our_mktemp();
  740.  
  741.     if (!(name = malloc((unsigned int)MAXPATHLEN)))
  742.         return((char *)NULL);
  743.  
  744.     if (!dir && (f = getenv("TMPDIR")) && !stat(f, &buf) &&
  745.                          (buf.st_mode&S_IFMT) == S_IFDIR &&
  746.              !can_access(f, WRITE_ACCESS|EXECUTE_ACCESS)) {
  747.         (void)strcpy(name, f);
  748.         goto done;
  749.     }
  750.  
  751.     if (dir && !stat(dir, &buf) &&
  752.                          (buf.st_mode&S_IFMT) == S_IFDIR &&
  753.                      !can_access(dir, WRITE_ACCESS|EXECUTE_ACCESS)) {
  754.         (void)strcpy(name, dir);
  755.         goto done;
  756.     }
  757.  
  758. #ifndef P_tmpdir
  759. #define    P_tmpdir    "/usr/tmp"
  760. #endif
  761.     if (!stat(P_tmpdir, &buf) &&
  762.                          (buf.st_mode&S_IFMT) == S_IFDIR &&
  763.              !can_access(P_tmpdir, WRITE_ACCESS|EXECUTE_ACCESS)) {
  764.         (void)strcpy(name, P_tmpdir);
  765.         goto done;
  766.     }
  767.  
  768.     if (!stat("/tmp", &buf) &&
  769.                          (buf.st_mode&S_IFMT) == S_IFDIR &&
  770.              !can_access("/tmp", WRITE_ACCESS|EXECUTE_ACCESS)) {
  771.         (void)strcpy(name, "/tmp");
  772.         goto done;
  773.     }
  774.     free((void *)name);
  775.     return((char *)NULL);
  776.  
  777. done:
  778.     if(*(f = &name[strlen(name) - 1]) != '/')
  779.       *++f = '/';
  780.  
  781.     f++;
  782.     if (prefix)
  783.       sstrcpy(&f, prefix);
  784.  
  785.     sstrcpy(&f, "XXXXXX");
  786.     return(our_mktemp(name));
  787. }
  788.  
  789.  
  790. /*
  791.  * This routine is derived from BSD4.3 code,
  792.  * Copyright (c) 1987 Regents of the University of California.
  793.  * All rights reserved.
  794.  *
  795.  * We use this instead of mktemp() since we know of at least one stupid
  796.  * mktemp() (AIX3.2) which breaks things.
  797.  */
  798. #if defined(LIBC_SCCS) && !defined(lint)
  799. static char sccsid[] = "@(#)mktemp.c    5.7 (Berkeley) 6/27/88";
  800. #endif /* LIBC_SCCS and not lint */
  801.  
  802. static
  803. _gettemp(as)
  804.     char    *as;
  805. {
  806.     extern int    errno;
  807.     register char    *start, *trv;
  808.     struct stat    sbuf;
  809.     unsigned    pid;
  810.  
  811.     pid = (unsigned)getpid();
  812.  
  813.     /* extra X's get set to 0's */
  814.     for (trv = as; *trv; ++trv);
  815.     while (*--trv == 'X') {
  816.         *trv = (pid % 10) + '0';
  817.         pid /= 10;
  818.     }
  819.  
  820.     /*
  821.      * check for write permission on target directory; if you have
  822.      * six X's and you can't write the directory, this will run for
  823.      * a *very* long time.
  824.      */
  825.     for (start = ++trv; trv > as && *trv != '/'; --trv);
  826.     if (*trv == '/') {
  827.         *trv = '\0';
  828.         if (stat(as, &sbuf) || !(sbuf.st_mode & S_IFDIR))
  829.             return(0);
  830.         *trv = '/';
  831.     }
  832.     else if (stat(".", &sbuf) == -1)
  833.         return(0);
  834.  
  835.     for (;;) {
  836.         if (stat(as, &sbuf))
  837.             return(errno == ENOENT ? 1 : 0);
  838.  
  839.         /* tricky little algorithm for backward compatibility */
  840.         for (trv = start;;) {
  841.             if (!*trv)
  842.                 return(0);
  843.             if (*trv == 'z')
  844.                 *trv++ = 'a';
  845.             else {
  846.                 if (isdigit(*trv))
  847.                     *trv = 'a';
  848.                 else
  849.                     ++*trv;
  850.                 break;
  851.             }
  852.         }
  853.     }
  854.     /*NOTREACHED*/
  855. }
  856.  
  857. char *
  858. our_mktemp(as)
  859.     char    *as;
  860. {
  861.     return(_gettemp(as) ? as : (char *)NULL);
  862. }
  863.  
  864.  
  865.  
  866. /*----------------------------------------------------------------------
  867.      Abort with a core dump
  868.  ----*/
  869. void
  870. coredump()
  871. {
  872.     abort();
  873. }
  874.  
  875.  
  876.  
  877. /*----------------------------------------------------------------------
  878.        Call system gethostname
  879.  
  880.   Args: hostname -- buffer to return host name in 
  881.         size     -- Size of buffer hostname is to be returned in
  882.  
  883.  Result: returns 0 if the hostname is correctly set,
  884.          -1 if not (and errno is set).
  885.  ----*/
  886. hostname(hostname,size) 
  887.     char *hostname;
  888.     int size;
  889. {
  890.     return(gethostname(hostname, size));
  891. }
  892.  
  893.  
  894.  
  895. /*----------------------------------------------------------------------
  896.        Get the current host and domain names
  897.  
  898.     Args: hostname   -- buffer to return the hostname in
  899.           hsize      -- size of buffer above
  900.           domainname -- buffer to return domain name in
  901.           dsize      -- size of buffer above
  902.  
  903.   Result: The system host and domain names are returned. If the full host
  904.           name is akbar.cac.washington.edu then the domainname is
  905.           cac.washington.edu.
  906.  
  907. On Internet connected hosts this look up uses /etc/hosts and DNS to
  908. figure all this out. On other less well connected machines some other
  909. file may be read. If there is no notion of a domain name the domain
  910. name may be left blank. On a PC where there really isn't a host name
  911. this should return blank strings. The .pinerc will take care of
  912. configuring the domain names. That is, this should only return the
  913. native system's idea of what the names are if the system has such
  914. a concept.
  915.  ----*/
  916. void
  917. getdomainnames(hostname, hsize, domainname, dsize)
  918.     char *hostname, *domainname;
  919.     int   hsize, dsize;
  920. {
  921.     char           *dn, hname[MAX_ADDRESS+1];
  922.     struct hostent *he;
  923.  
  924.     gethostname(hname, MAX_ADDRESS);
  925.  
  926.     he = gethostbyname(hname);
  927.  
  928.     if(he == NULL && strlen(hname) == 0) {
  929.         strcpy(hostname, "");
  930.     } else if(he == NULL) {
  931.         strncpy(hostname, hname, hsize - 1);
  932.     } else {
  933.         strncpy(hostname, he->h_name, hsize-1);
  934.     }
  935.     hostname[hsize-1] = '\0';
  936.  
  937.  
  938.     if((dn = strindex(hostname, '.')) != NULL) {
  939.         strncpy(domainname, dn+1, dsize-1);
  940.     } else {
  941.         strncpy(domainname, hostname, dsize-1);
  942.     }
  943.     domainname[dsize-1] = '\0';
  944. }
  945.  
  946.  
  947.  
  948. /*----------------------------------------------------------------------
  949.        Return canonical form of host name ala c-client (UNIX version).
  950.  
  951.    Args: host      -- The host name
  952.  
  953.  Result: Canonical form, or input argument (worst case)
  954.  ----*/
  955. char *
  956. canonical_name(host)
  957.     char *host;
  958. {
  959.     struct hostent *hent;
  960.     char hostname[MAILTMPLEN];
  961.     char tmp[MAILTMPLEN];
  962.     extern char *lcase();
  963.                                 /* domain literal is easy */
  964.     if (host[0] == '[' && host[(strlen (host))-1] == ']')
  965.       return host;
  966.  
  967.     strcpy (hostname,host);       /* UNIX requires lowercase */
  968.                                 /* lookup name, return canonical form */
  969.     return (hent = gethostbyname (lcase (strcpy (tmp,host)))) ?
  970.       hent->h_name : host;
  971. }
  972.  
  973.  
  974.  
  975. /*----------------------------------------------------------------------
  976.      This routine returns 1 if job control is available.  Note, thiis
  977.      could be some type of fake job control.  It doesn't have to be
  978.      real BSD-style job control.
  979.   ----*/
  980. have_job_control()
  981. {
  982.     return 1;
  983. }
  984.  
  985.  
  986. /*----------------------------------------------------------------------
  987.     If we don't have job control, this routine is never called.
  988.   ----*/
  989. stop_process()
  990. {
  991.     kill(0, SIGSTOP); 
  992. }
  993.  
  994.  
  995.  
  996. extern char *sys_errlist[];
  997.  
  998. /*----------------------------------------------------------------------
  999.        Return string describing the error
  1000.  
  1001.    Args: errnumber -- The system error number (errno)
  1002.  
  1003.  Result:  long string describing the error is returned
  1004.   ----*/
  1005. char *
  1006. error_description(errnumber)
  1007.     int errnumber;
  1008. {
  1009.     static char buffer[50];
  1010.  
  1011.     strcpy(buffer, sys_errlist[errnumber]);
  1012.  
  1013.     return ( (char *) buffer);
  1014. }
  1015.  
  1016.  
  1017.  
  1018. /*----------------------------------------------------------------------
  1019.       Pull the name out of the gcos field if we have that sort of /etc/passwd
  1020.  
  1021.    Args: gcos_field --  The long name or GCOS field to be parsed
  1022.          logname    --  Replaces occurances of & with logname string
  1023.  
  1024.  Result: returns pointer to buffer with name
  1025.   ----*/
  1026. static char *
  1027. gcos_name(gcos_field, logname)
  1028.     char *logname, *gcos_field;
  1029. {
  1030.     static char fullname[MAX_FULLNAME+1];
  1031.     register char *fncp, *gcoscp, *lncp, *end;
  1032.  
  1033.     /* full name is all chars up to first ',' (or whole gcos, if no ',') */
  1034.     /* replace any & with logname in upper case */
  1035.  
  1036.     for(fncp = fullname, gcoscp= gcos_field, end = fullname + MAX_FULLNAME - 1;
  1037.         (*gcoscp != ',' && *gcoscp != '\0' && fncp != end);
  1038.     gcoscp++) {
  1039.  
  1040.     if(*gcoscp == '&') {
  1041.         for(lncp = logname; *lncp; fncp++, lncp++)
  1042.         *fncp = toupper(*lncp);
  1043.     } else {
  1044.         *fncp++ = *gcoscp;
  1045.     }
  1046.     }
  1047.     
  1048.     *fncp = '\0';
  1049.     return(fullname);
  1050. }
  1051.  
  1052.  
  1053. /*----------------------------------------------------------------------
  1054.       Fill in homedir, login, and fullname for the logged in user.
  1055.       These are all pointers to static storage so need to be copied
  1056.       in the caller.
  1057.  
  1058.  Args: ui    -- struct pointer to pass back answers
  1059.  
  1060.  Result: fills in the fields
  1061.   ----*/
  1062. void
  1063. get_user_info(ui)
  1064.     struct user_info *ui;
  1065. {
  1066.     struct passwd *unix_pwd;
  1067.  
  1068.     unix_pwd = getpwuid(getuid());
  1069.     if(unix_pwd == NULL) {
  1070.       ui->homedir = cpystr("");
  1071.       ui->login = cpystr("");
  1072.       ui->fullname = cpystr("");
  1073.     }else {
  1074.       ui->homedir = cpystr(unix_pwd->pw_dir);
  1075.       ui->login = cpystr(unix_pwd->pw_name);
  1076.       ui->fullname = cpystr(gcos_name(unix_pwd->pw_gecos, unix_pwd->pw_name));
  1077.     }
  1078. }
  1079.  
  1080.  
  1081. /*----------------------------------------------------------------------
  1082.       Look up a userid on the local system and return rfc822 address
  1083.  
  1084.  Args: name  -- possible login name on local system
  1085.  
  1086.  Result: returns NULL or pointer to alloc'd string rfc822 address.
  1087.   ----*/
  1088. char *
  1089. local_name_lookup(name)
  1090.     char *name;
  1091. {
  1092.     struct passwd *pw = getpwnam(name);
  1093.  
  1094.     if(pw == NULL)
  1095.       return((char *)NULL);
  1096.  
  1097.     return(cpystr(gcos_name(pw->pw_gecos, name)));
  1098. }
  1099.  
  1100.  
  1101.  
  1102. /*----------------------------------------------------------------------
  1103.        Call the system to change the passwd
  1104.  
  1105. It would be nice to talk to the passwd program via a pipe or ptty so the
  1106. user interface could be consistent, but we can't count on the the prompts
  1107. and responses from the passwd program to be regular so we just let the user 
  1108. type at the passwd program with some screen space, hope he doesn't scroll 
  1109. off the top and repaint when he's done.
  1110.  ----*/        
  1111. change_passwd()
  1112. {
  1113.     char cmd_buf[100];
  1114.  
  1115.     ClearLines(1, ps_global->ttyo->screen_rows - 1);
  1116.  
  1117.     MoveCursor(5, 0);
  1118.     fflush(stdout);
  1119.  
  1120.     Raw(0);
  1121.     strcpy(cmd_buf, PASSWD_PROG);
  1122.     system(cmd_buf);
  1123.     sleep(3);
  1124.     Raw(1);
  1125. }
  1126.  
  1127.  
  1128.  
  1129. /*----------------------------------------------------------------------
  1130.        Can we display this type/subtype?
  1131.  
  1132.    Args: type       -- the MIME type to check
  1133.          subtype    -- the MIME subtype
  1134.          params     -- parameters
  1135.      use_viewer -- tell caller he should run external viewer cmd to view
  1136.  
  1137.  Result: returns 1 if the type is displayable, 0 otherwise.
  1138.  Note: we always return 1 for type text and type message, but sometimes
  1139.        we set use_viewer and sometimes we don't.
  1140.  ----*/
  1141. mime_can_display(type, subtype, params, use_viewer)
  1142. int       type;
  1143. char      *subtype;
  1144. PARAMETER *params;
  1145. int       *use_viewer;
  1146. {
  1147.     int rv;
  1148.  
  1149.     /* give mailcap a crack at everything first */
  1150.     if(mailcap_can_display(type, subtype, params)){
  1151.     if(use_viewer)
  1152.       *use_viewer = 1;
  1153.  
  1154.     rv = 1;
  1155.     }
  1156.     else{
  1157.     if(use_viewer)
  1158.       *use_viewer = 0;
  1159.  
  1160.     switch(type){
  1161.  
  1162.       /* if mailcap didn't want to handle these, we will */
  1163.       case TYPETEXT:
  1164.       case TYPEMESSAGE:
  1165.         rv = 1;
  1166.         break;
  1167.  
  1168.       case TYPEAPPLICATION:
  1169.         if(subtype && !strucmp(subtype, "DIRECTORY"))
  1170.           rv = 1;
  1171.         else
  1172.           rv = 0;
  1173.  
  1174.         break;
  1175.  
  1176.       default:
  1177.         rv = 0;
  1178.         break;
  1179.     }
  1180.     }
  1181.  
  1182.     return(rv);
  1183. }
  1184.  
  1185.  
  1186.  
  1187. /*----------------------------------------------------------------------
  1188.    This is just a call to the ANSI C fgetpos function.
  1189.   ----*/
  1190. fget_pos(stream, ptr)
  1191. FILE *stream;
  1192. fpos_t *ptr;
  1193. {
  1194.     return(fgetpos(stream, ptr));
  1195. }
  1196.  
  1197.  
  1198. /*----------------------------------------------------------------------
  1199.    This is just a call to the ANSI C fsetpos function.
  1200.   ----*/
  1201. fset_pos(stream, ptr)
  1202. FILE *stream;
  1203. fpos_t *ptr;
  1204. {
  1205.     return(fsetpos(stream, ptr));
  1206. }
  1207.  
  1208.  
  1209.  
  1210. /*======================================================================
  1211.     pipe
  1212.     
  1213.     Initiate I/O to and from a process.  These functions are similar to 
  1214.     popen and pclose, but both an incoming stream and an output file are 
  1215.     provided.
  1216.    
  1217.  ====*/
  1218.  
  1219. #ifndef    STDIN_FILENO
  1220. #define    STDIN_FILENO    0
  1221. #endif
  1222. #ifndef    STDOUT_FILENO
  1223. #define    STDOUT_FILENO    1
  1224. #endif
  1225. #ifndef    STDERR_FILENO
  1226. #define    STDERR_FILENO    2
  1227. #endif
  1228.  
  1229.  
  1230. /*
  1231.  * Defs to help fish child's exit status out of wait(2)
  1232.  */
  1233. #ifdef    HAVE_WAIT_UNION
  1234. #define WaitType    union wait
  1235. #ifndef    WIFEXITED
  1236. #define    WIFEXITED(X)    (!(X).w_termsig)    /* child exit by choice */
  1237. #endif
  1238. #ifndef    WEXITSTATUS
  1239. #define    WEXITSTATUS(X)    (X).w_retcode        /* childs chosen exit value */
  1240. #endif
  1241. #else
  1242. #define    WaitType    int
  1243. #ifndef    WIFEXITED
  1244. #define    WIFEXITED(X)    (!((X) & 0xff))        /* low bits tell how it died */
  1245. #endif
  1246. #ifndef    WEXITSTATUS
  1247. #define    WEXITSTATUS(X)    (((X) >> 8) & 0xff)    /* high bits tell exit value */
  1248. #endif
  1249. #endif
  1250.  
  1251.  
  1252. /*
  1253.  * Global's to helpsignal handler tell us child's status has changed...
  1254.  */
  1255. short    child_signalled;
  1256. short    child_jump = 0;
  1257. jmp_buf child_state;
  1258.  
  1259.  
  1260.  
  1261. /*----------------------------------------------------------------------
  1262.      Spawn a child process and optionally connect read/write pipes to it
  1263.  
  1264.   Args: command -- string to hand the shell
  1265.     outfile -- address of pointer containing file to receive output
  1266.     errfile -- address of pointer containing file to receive error output
  1267.     mode -- mode for type of shell, signal protection etc...
  1268.   Returns: pointer to alloc'd PIPE_S on success, NULL otherwise
  1269.  
  1270.   The outfile is either NULL, a pointer to a NULL value, or a pointer
  1271.   to the requested name for the output file.  In the pointer-to-NULL case
  1272.   the caller doesn't care about the name, but wants to see the pipe's
  1273.   results so we make one up.  It's up to the programmer to make sure
  1274.   the free storage containing the name is cleaned up.
  1275.  
  1276.   Mode bits serve several purposes.
  1277.     PIPE_WRITE tells us we need to open a pipe to write the child's
  1278.     stdin.
  1279.     PIPE_READ tells us we need to open a pipe to read from the child's
  1280.     stdout/stderr.  *NOTE*  Having neither of the above set means 
  1281.     we're not setting up any pipes, just forking the child and exec'ing
  1282.     the command.  Also, this takes precedence over any named outfile.
  1283.     PIPE_STDERR means we're to tie the childs stderr to the same place
  1284.     stdout is going.  *NOTE* This only makes sense then if PIPE_READ
  1285.     or an outfile is provided.  Also, this takes precedence over any
  1286.     named errfile.
  1287.     PIPE_PROT means to protect the child from the usual nasty signals
  1288.     that might cause premature death.  Otherwise, the default signals are
  1289.     set so the child can deal with the nasty signals in its own way.     
  1290.     PIPE_NOSHELL means we're to exec the command without the aid of
  1291.     a system shell.  *NOTE* This negates the affect of PIPE_USER.
  1292.     PIPE_USER means we're to try executing the command in the user's
  1293.     shell.  Right now we only look in the environment, but that may get
  1294.     more sophisticated later.
  1295.     PIPE_RESET means we reset the terminal mode to what it was before
  1296.     we started pine and then exec the command.
  1297.  ----*/
  1298. PIPE_S *
  1299. open_system_pipe(command, outfile, errfile, mode)
  1300.     char  *command;
  1301.     char **outfile, **errfile;
  1302.     int    mode;
  1303. {
  1304.     PIPE_S *syspipe = NULL;
  1305.     char    shellpath[32], *shell;
  1306.     int     p[2], oparentd = -1, ochildd = -1, iparentd = -1, ichildd = -1;
  1307.  
  1308.     dprint(5, (debugfile, "Opening pipe: \"%s\" (%s%s%s%s%s%s)\n",command,
  1309.            (mode & PIPE_WRITE)   ? "W":"", (mode & PIPE_READ)  ? "R":"",
  1310.            (mode & PIPE_NOSHELL) ? "N":"", (mode & PIPE_PROT)  ? "P":"",
  1311.            (mode & PIPE_USER)    ? "U":"", (mode & PIPE_RESET) ? "T":""));
  1312.  
  1313.     if(!(mode & PIPE_READ)){
  1314.     if(outfile && !*outfile)
  1315.       *outfile = temp_nam(NULL, "pine_p");    /* asked for, but not named? */
  1316.  
  1317.     if(errfile && !*errfile)
  1318.       *errfile = temp_nam(NULL, "pine_p");    /* ditto */
  1319.     }
  1320.  
  1321.     if(mode & (PIPE_WRITE | PIPE_READ)){
  1322.     if(mode & PIPE_WRITE){
  1323.         pipe(p);                /* alloc pipe to write child */
  1324.         oparentd = p[STDOUT_FILENO];
  1325.         ichildd  = p[STDIN_FILENO];
  1326.     }
  1327.  
  1328.     if(mode & PIPE_READ){
  1329.         pipe(p);                /* alloc pipe to read child */
  1330.         iparentd = p[STDIN_FILENO];
  1331.         ochildd  = p[STDOUT_FILENO];
  1332.     }
  1333.     }
  1334.     else{
  1335.     flush_status_messages(0);        /* just clean up display */
  1336.     ClearScreen();
  1337.     fflush(stdout);
  1338.     }
  1339.  
  1340.     syspipe = (PIPE_S *)fs_get(sizeof(PIPE_S));
  1341.     memset(syspipe, 0, sizeof(PIPE_S));
  1342.  
  1343.     if((syspipe->mode = mode) & PIPE_RESET)
  1344.       Raw(0);
  1345.  
  1346. #ifdef    SIGCHLD
  1347.     /*
  1348.      * Prepare for demise of child.  Use SIGCHLD if it's available so
  1349.      * we can do useful things, like keep the IMAP stream alive, while
  1350.      * we're waiting on the child.
  1351.      */
  1352.     child_signalled = child_jump = 0;
  1353.     (void)signal(SIGCHLD,  child_signal);
  1354. #endif
  1355.  
  1356.     if((syspipe->pid = vfork()) == 0){
  1357.      /* reset child's handlers in requested fashion... */
  1358.     (void)signal(SIGINT,  (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
  1359.     (void)signal(SIGQUIT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
  1360.     (void)signal(SIGHUP,  (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
  1361. #ifdef    SIGCHLD
  1362.     (void) signal(SIGCHLD,  SIG_DFL);
  1363. #endif
  1364.  
  1365.     /* if parent isn't reading, and we have a filename to write */
  1366.     if(!(mode & PIPE_READ) && outfile){    /* connect output to file */
  1367.         int output = creat(*outfile, 0600);
  1368.         dup2(output, STDOUT_FILENO);
  1369.         if(mode & PIPE_STDERR)
  1370.           dup2(output, STDERR_FILENO);
  1371.         else if(errfile)
  1372.           dup2(creat(*errfile, 0600), STDERR_FILENO);
  1373.     }
  1374.  
  1375.     if(mode & PIPE_WRITE){            /* connect process input */
  1376.         close(oparentd);
  1377.         dup2(ichildd, STDIN_FILENO);    /* tie stdin to pipe */
  1378.         close(ichildd);
  1379.     }
  1380.  
  1381.     if(mode & PIPE_READ){            /* connect process output */
  1382.         close(iparentd);
  1383.         dup2(ochildd, STDOUT_FILENO);    /* tie std{out,err} to pipe */
  1384.         if(mode & PIPE_STDERR)
  1385.           dup2(ochildd, STDERR_FILENO);
  1386.         else if(errfile)
  1387.           dup2(creat(*errfile, 0600), STDERR_FILENO);
  1388.  
  1389.         close(ochildd);
  1390.     }
  1391.  
  1392.     if(mode & PIPE_NOSHELL){
  1393.         char   **argv, **ap, *p, *cmd;
  1394.         size_t   n;
  1395.  
  1396.         /* parse the arguments into a list */
  1397.         for(cmd = cpystr(command); *cmd && isspace(*cmd); cmd++)
  1398.           ;                    /* swallow leading ws */
  1399.  
  1400.         for(p = cmd, n = 2; *p; p++)    /* count the args */
  1401.           if(isspace(*p) && *(p+1) && !isspace(*(p+1)))
  1402.         n++;
  1403.  
  1404.         argv = ap = (char **)fs_get(n * sizeof(char *));
  1405.         memset(argv, 0, n * sizeof(char *));
  1406.         for(p = cmd; *p && !isspace(*p); p++)
  1407.           ;
  1408.  
  1409.         if(*p)                /* tie off command name */
  1410.           *p++ = '\0';
  1411.  
  1412.         *ap++ = cpystr(cmd);
  1413.         while(*p){                /* collect args */
  1414.         while(*p && isspace(*p))
  1415.           *p++ = '\0';
  1416.  
  1417.         *ap++ = (*p) ? p : NULL;
  1418.         while(*p && !isspace(*p))
  1419.           p++;
  1420.         }
  1421.  
  1422.         execvp(cmd, argv);
  1423.     }
  1424.     else{
  1425.         if(mode & PIPE_USER){
  1426.         char *env, *sh;
  1427.         if((env = getenv("SHELL")) && (sh = strrchr(env, '/'))){
  1428.             shell = sh + 1;
  1429.             strcpy(shellpath, env);
  1430.         }
  1431.         else{
  1432.             shell = "csh";
  1433.             strcpy(shellpath, "/bin/csh");
  1434.         }
  1435.         }
  1436.         else{
  1437.         shell = "sh";
  1438.         strcpy(shellpath, "/bin/sh");
  1439.         }
  1440.  
  1441.         execl(shellpath, shell, command ? "-c" : 0, command, 0);
  1442.     }
  1443.  
  1444.     fprintf(stderr, "Can't exec %s\nReason: %s",
  1445.         command, error_description(errno));
  1446.     _exit(-1);
  1447.     }
  1448.  
  1449.     if(syspipe->pid > 0){
  1450.     syspipe->isig = signal(SIGINT,  SIG_IGN); /* Reset handlers to make */
  1451.     syspipe->qsig = signal(SIGQUIT, SIG_IGN); /* sure we don't come to  */
  1452.     syspipe->hsig = signal(SIGHUP,  SIG_IGN); /* a premature end...     */
  1453.  
  1454.     if(mode & PIPE_WRITE){
  1455.         close(ichildd);
  1456.         syspipe->ofilep = fdopen(oparentd, "w");
  1457.     }
  1458.  
  1459.     if(mode & PIPE_READ){
  1460.         close(ochildd);
  1461.         syspipe->ifilep = fdopen(iparentd, "r");
  1462.     }
  1463.  
  1464.     dprint(5, (debugfile, "PID: %d, COMMAND: %s\n",syspipe->pid,command));
  1465.     }
  1466.     else{
  1467.     if(mode & (PIPE_WRITE | PIPE_READ)){
  1468.         if(mode & PIPE_WRITE){
  1469.         close(oparentd);
  1470.         close(ichildd);
  1471.         }
  1472.  
  1473.         if(mode & PIPE_READ){
  1474.         close(iparentd);
  1475.         close(ochildd);
  1476.         }
  1477.     }
  1478.     else{
  1479.         ClearScreen();
  1480.         ps_global->mangled_screen = 1;
  1481.     }
  1482.  
  1483.     if(mode & PIPE_RESET)
  1484.       Raw(1);
  1485.  
  1486. #ifdef    SIGCHLD
  1487.     (void) signal(SIGCHLD,  SIG_DFL);
  1488. #endif
  1489.  
  1490.     q_status_message1(SM_ORDER,3,3, "Error executing external command: %s",
  1491.               error_description(errno));
  1492.     fs_give((void **)&syspipe);
  1493.     if(outfile)
  1494.       fs_give((void **)outfile);
  1495.  
  1496.     dprint(1, (debugfile, "CAN'T FORK FOR COMMAND: %s\n", command));
  1497.     }
  1498.  
  1499.     return(syspipe);
  1500. }
  1501.  
  1502.  
  1503.  
  1504. /*----------------------------------------------------------------------
  1505.     Close pipe previously allocated and wait for child's death
  1506.  
  1507.   Args: syspipe -- address of pointer to struct returned by open_system_pipe
  1508.   Returns: returns exit status of child or -1 if invalid syspipe
  1509.  ----*/
  1510. int
  1511. close_system_pipe(syspipe)
  1512.     PIPE_S **syspipe;
  1513. {
  1514.     WaitType stat;
  1515.     int         status;
  1516.  
  1517.     if(!syspipe || !*syspipe)
  1518.       return(-1);
  1519.  
  1520.     if((*syspipe)->ofilep)
  1521.       fclose((*syspipe)->ofilep);
  1522.  
  1523.     if((*syspipe)->ifilep)
  1524.       fclose((*syspipe)->ifilep);
  1525.  
  1526. #ifdef    SIGCHLD
  1527.     {
  1528.     SigType (*alarm_sig)();
  1529.     int    old_cue = F_ON(F_SHOW_DELAY_CUE, ps_global);
  1530.  
  1531.     /*
  1532.      * remember the current SIGALRM handler, and make sure it's
  1533.      * installed when we're finished just in case the longjmp
  1534.      * out of the SIGCHLD handler caused sleep() to lose it.
  1535.      * Don't pay any attention to that man behind the curtain.
  1536.      */
  1537.     alarm_sig = signal(SIGALRM, SIG_IGN);
  1538.     (void) signal(SIGALRM, alarm_sig);
  1539.     F_SET(F_SHOW_DELAY_CUE, ps_global, 0);
  1540.     ps_global->noshow_timeout = 1;
  1541.     while(!child_signalled){
  1542.         new_mail(0, 2, 0);        /* wake up and prod server */
  1543.  
  1544.         if(!child_signalled){
  1545.         if(setjmp(child_state) == 0){
  1546.             child_jump = 1;    /* prepare to wake up */
  1547.             sleep(600);        /* give it 5mins to happend */
  1548.         }
  1549. #ifdef POSIX_SIGNALS
  1550.         else
  1551.           sigrelse(SIGCHLD);    /* unblock signal after longjmp */
  1552. #endif
  1553.         }
  1554.  
  1555.         child_jump = 0;
  1556.     }
  1557.  
  1558.     ps_global->noshow_timeout = 0;
  1559.     F_SET(F_SHOW_DELAY_CUE, ps_global, old_cue);
  1560.     (void) signal(SIGALRM, alarm_sig);
  1561.     (void) signal(SIGCHLD,  SIG_DFL);
  1562.     }
  1563. #endif
  1564.  
  1565.     /*
  1566.      * Call c-client's pid reaper to wait() on the demise of our child,
  1567.      * then fish out its exit status...
  1568.      */
  1569.     grim_pid_reap_status((*syspipe)->pid, 0, &stat);
  1570.     status = WIFEXITED(stat) ? WEXITSTATUS(stat) : -1;
  1571.  
  1572.     /*
  1573.      * restore original handlers...
  1574.      */
  1575.     (void)signal(SIGINT,  (*syspipe)->isig);
  1576.     (void)signal(SIGHUP,  (*syspipe)->hsig);
  1577.     (void)signal(SIGQUIT, (*syspipe)->qsig);
  1578.  
  1579.     if((*syspipe)->mode & PIPE_RESET)        /* restore our tty modes */
  1580.       Raw(1);
  1581.  
  1582.     if(!((*syspipe)->mode & (PIPE_WRITE | PIPE_READ))){
  1583.     ClearScreen();                /* No I/O to forked child */
  1584.     ps_global->mangled_screen = 1;
  1585.     }
  1586.  
  1587.     fs_give((void **)syspipe);
  1588.     return(status);
  1589. }
  1590.  
  1591. /*----------------------------------------------------------------------
  1592.     Routines used to hand off messages to local agents for sending/posting
  1593.  
  1594.  The two exported routines are:
  1595.  
  1596.     1) smtp_command()  -- used to get local transport agent to invoke
  1597.     2) post_handoff()  -- used to pass messages to local posting agent
  1598.  
  1599.  ----*/
  1600.  
  1601.  
  1602.  
  1603. /*
  1604.  * Protos for "sendmail" internal functions
  1605.  */
  1606. static char *mta_parse_post PROTO((METAENV *, BODY *, char *, char *));
  1607. static long  pine_pipe_soutr_nl PROTO((void *, char *));
  1608.  
  1609.  
  1610.  
  1611. /* ----------------------------------------------------------------------
  1612.    Figure out command to start local SMTP agent
  1613.  
  1614.   Args: errbuf   -- buffer for reporting errors (assumed non-NULL)
  1615.  
  1616.   Returns an alloc'd copy of the local SMTP agent invocation or NULL
  1617.  
  1618.   ----*/
  1619. char *
  1620. smtp_command(errbuf)
  1621.     char *errbuf;
  1622. {
  1623. #if    defined(SENDMAIL) && defined(SENDMAILFLAGS)
  1624.     char tmp[256];
  1625.  
  1626.     sprintf(tmp, "%s %s", SENDMAIL, SENDMAILFLAGS);
  1627.     return(cpystr(tmp));
  1628. #else
  1629.     strcpy(errbuf, "No default posting command.");
  1630.     return(NULL);
  1631. #endif
  1632. }
  1633.  
  1634.  
  1635.  
  1636. /*----------------------------------------------------------------------
  1637.    Hand off given message to local posting agent
  1638.  
  1639.   Args: envelope -- The envelope for the BCC and debugging
  1640.         header   -- The text of the message header
  1641.         errbuf   -- buffer for reporting errors (assumed non-NULL)
  1642.      
  1643.    ----*/
  1644. int
  1645. mta_handoff(header, body, errbuf)
  1646.     METAENV    *header;
  1647.     BODY       *body;
  1648.     char       *errbuf;
  1649. {
  1650.     char cmd_buf[256], *cmd = NULL;
  1651.  
  1652.     /*
  1653.      * A bit of complicated policy implemented here.
  1654.      * There are two posting variables sendmail-path and smtp-server.
  1655.      * Precedence is in that order.
  1656.      * They can be set one of 4 ways: fixed, command-line, user, or globally.
  1657.      * Precedence is in that order.
  1658.      * Said differently, the order goes something like what's below.
  1659.      * 
  1660.      * NOTE: the fixed/command-line/user precendence handling is also
  1661.      *         indicated by what's pointed to by ps_global->VAR_*, but since
  1662.      *         that also includes the global defaults, it's not sufficient.
  1663.      */
  1664.  
  1665.     if(ps_global->FIX_SENDMAIL_PATH
  1666.        && ps_global->FIX_SENDMAIL_PATH[0]){
  1667.     cmd = ps_global->FIX_SENDMAIL_PATH;
  1668.     }
  1669.     else if(!(ps_global->FIX_SMTP_SERVER
  1670.           && ps_global->FIX_SMTP_SERVER[0])){
  1671.     if(ps_global->COM_SENDMAIL_PATH
  1672.        && ps_global->COM_SENDMAIL_PATH[0]){
  1673.         cmd = ps_global->COM_SENDMAIL_PATH;
  1674.     }
  1675.     else if(!(ps_global->COM_SMTP_SERVER
  1676.           && ps_global->COM_SMTP_SERVER[0])){
  1677.         if(ps_global->USR_SENDMAIL_PATH
  1678.            && ps_global->USR_SENDMAIL_PATH[0]){
  1679.         cmd = ps_global->USR_SENDMAIL_PATH;
  1680.         }
  1681.         else if(!(ps_global->USR_SMTP_SERVER
  1682.               && ps_global->USR_SMTP_SERVER[0])){
  1683.         if(ps_global->GLO_SENDMAIL_PATH
  1684.            && ps_global->GLO_SENDMAIL_PATH[0]){
  1685.             cmd = ps_global->GLO_SENDMAIL_PATH;
  1686.         }
  1687. #ifdef    DF_SENDMAIL_PATH
  1688.         /*
  1689.          * This defines the default method of posting.  So,
  1690.          * unless we're told otherwise use it...
  1691.          */
  1692.         else if(!(ps_global->GLO_SMTP_SERVER
  1693.               && ps_global->GLO_SMTP_SERVER[0])){
  1694.             strcpy(cmd = cmd_buf, DF_SENDMAIL_PATH);
  1695.         }
  1696. #endif
  1697.         }
  1698.     }
  1699.     }
  1700.  
  1701.     *errbuf = '\0';
  1702.     if(cmd){
  1703.     dprint(4, (debugfile, "call_mailer via cmd: %s\n", cmd));
  1704.  
  1705.     (void) mta_parse_post(header, body, cmd, errbuf);
  1706.     return(1);
  1707.     }
  1708.     else
  1709.       return(0);
  1710. }
  1711.  
  1712.  
  1713.  
  1714. /*----------------------------------------------------------------------
  1715.    Hand off given message to local posting agent
  1716.  
  1717.   Args: envelope -- The envelope for the BCC and debugging
  1718.         header   -- The text of the message header
  1719.         errbuf   -- buffer for reporting errors (assumed non-NULL)
  1720.      
  1721.   Fork off mailer process and pipe the message into it
  1722.   Called to post news via Inews when NNTP is unavailable
  1723.   
  1724.    ----*/
  1725. char *
  1726. post_handoff(header, body, errbuf)
  1727.     METAENV    *header;
  1728.     BODY       *body;
  1729.     char       *errbuf;
  1730. {
  1731.     char *err = NULL;
  1732. #ifdef    SENDNEWS
  1733.     char *s;
  1734.  
  1735.     if(s = strstr(header->env->date," (")) /* fix the date format for news */
  1736.       *s = '\0';
  1737.  
  1738.     if(err = mta_parse_post(header, body, SENDNEWS, errbuf))
  1739.       sprintf(err = errbuf, "News not posted: \"%s\": %s", SENDNEWS, err);
  1740.  
  1741.     if(s)
  1742.       *s = ' ';                /* restore the date */
  1743.  
  1744. #else /* !SENDNEWS */  /* this is the default case */
  1745.     sprintf(err = errbuf, "Can't post, NNTP-server must be defined!");
  1746. #endif /* !SENDNEWS */
  1747.     return(err);
  1748. }
  1749.  
  1750.  
  1751.  
  1752. /*----------------------------------------------------------------------
  1753.    Hand off message to local MTA; it parses recipients from 822 header
  1754.  
  1755.   Args: header -- struct containing header data
  1756.         body  -- struct containing message body data
  1757.     cmd -- command to use for handoff (%s says where file should go)
  1758.     errs -- pointer to buf to hold errors
  1759.  
  1760.    ----*/
  1761. static char *
  1762. mta_parse_post(header, body, cmd, errs)
  1763.     METAENV *header;
  1764.     BODY    *body;
  1765.     char    *cmd;
  1766.     char    *errs;
  1767. {
  1768.     char c, *p, *result = NULL;
  1769.     int  rv;
  1770.     PIPE_S *pipe;
  1771.  
  1772.     dprint(1, (debugfile, "=== mta_parse_post(%s) ===\n", cmd));
  1773.  
  1774.     /* tie off cmd */
  1775.     for(p = cmd; (c = *p) && !isspace(*p); p++)
  1776.       ;
  1777.  
  1778.     *p = *errs = '\0';
  1779.     rv = can_access(cmd, EXECUTE_ACCESS);
  1780.     *p = c;
  1781.     if(!rv){
  1782.     if(pipe = open_system_pipe(cmd, &result, NULL,
  1783.             PIPE_STDERR | PIPE_WRITE | PIPE_PROT | PIPE_NOSHELL)){
  1784.         if(!pine_rfc822_output(header, body, pine_pipe_soutr_nl,
  1785.                    (TCPSTREAM *) pipe))
  1786.           strcpy(errs, "Error posting.");
  1787.  
  1788.         if(close_system_pipe(&pipe) && !*errs){
  1789.         sprintf(errs, "Posting program %s returned error", cmd);
  1790.         if(result)
  1791.           display_output_file(result, "POSTING ERRORS", errs);
  1792.         }
  1793.     }
  1794.     else
  1795.       sprintf(errs, "Error running cmd: %s", cmd);
  1796.  
  1797.     if(result){
  1798.         unlink(result);
  1799.         fs_give((void **)&result);
  1800.     }
  1801.     }
  1802.     else
  1803.       sprintf(errs, "Error with \"%s\" : %s", cmd,
  1804.           (errno > 0) ? error_description(errno) : "not executable");
  1805.  
  1806.     return(*errs ? errs : NULL);
  1807. }
  1808.  
  1809.  
  1810. /* 
  1811.  * pine_pipe_soutr - Replacement for tcp_soutr that writes one of our
  1812.  *             pipes rather than a tcp stream
  1813.  */
  1814. static long
  1815. pine_pipe_soutr_nl (stream,s)
  1816.      void *stream;
  1817.      char *s;
  1818. {
  1819.     long    rv = T;
  1820.     char   *p;
  1821.     size_t  n;
  1822.  
  1823.     while(*s && rv){
  1824.     /* map CR LF ? */
  1825.     if(n = (p = strstr(s, "\015\012")) ? p - s : strlen(s))
  1826.       do
  1827.         rv = fwrite(s, n, (size_t) 1, ((PIPE_S *)stream)->ofilep);
  1828.       while(!rv && ferror(((PIPE_S *)stream)->ofilep) && errno == EINTR);
  1829.  
  1830.     if(p && rv){
  1831.         s = p + 2;
  1832.         do                    /* write UNIX EOL */
  1833.           rv = fwrite("\n", (size_t)1, (size_t)1,
  1834.               ((PIPE_S *)stream)->ofilep);
  1835.         while(!rv && ferror(((PIPE_S *)stream)->ofilep)
  1836.           && errno == EINTR);
  1837.     }
  1838.     else
  1839.       break;
  1840.     }
  1841.  
  1842.     return(rv);
  1843. }
  1844.  
  1845. /* ----------------------------------------------------------------------
  1846.    Execute the given mailcap command
  1847.  
  1848.   Args: cmd           -- the command to execute
  1849.     image_file    -- the file the data is in
  1850.     needsterminal -- does this command want to take over the terminal?
  1851.   ----*/
  1852. void
  1853. exec_mailcap_cmd(cmd, image_file, needsterminal)
  1854. char *cmd;
  1855. char *image_file;
  1856. int   needsterminal;
  1857. {
  1858.     char   *command = NULL,
  1859.        *result_file = NULL,
  1860.        *p;
  1861.     char  **r_file_h;
  1862.     PIPE_S *syspipe;
  1863.     int     mode;
  1864.  
  1865.     p = command = (char *)fs_get((32 + strlen(cmd) + (2*strlen(image_file)))
  1866.                  * sizeof(char));
  1867.     if(!needsterminal)  /* put in background if it doesn't need terminal */
  1868.       *p++ = '(';
  1869.     sprintf(p, "%s ; rm -f %s", cmd, image_file);
  1870.     p += strlen(p);
  1871.     if(!needsterminal){
  1872.     *p++ = ')';
  1873.     *p++ = ' ';
  1874.     *p++ = '&';
  1875.     }
  1876.     *p++ = '\n';
  1877.     *p   = '\0';
  1878.     dprint(9, (debugfile, "exec_mailcap_cmd: command=%s\n", command));
  1879.  
  1880.     mode = PIPE_RESET;
  1881.     if(needsterminal)
  1882.       r_file_h = NULL;
  1883.     else{
  1884.     mode       |= PIPE_WRITE | PIPE_STDERR;
  1885.     result_file = temp_nam(NULL, "pine_cmd");
  1886.     r_file_h    = &result_file;
  1887.     }
  1888.  
  1889.     if(syspipe = open_system_pipe(command, r_file_h, NULL, mode)){
  1890.     close_system_pipe(&syspipe);
  1891.     if(needsterminal)
  1892.       q_status_message(SM_ORDER, 0, 4, "VIEWER command completed");
  1893.     else
  1894.       display_output_file(result_file, "VIEWER", " command launched");
  1895.     }
  1896.     else
  1897.       q_status_message1(SM_ORDER, 3, 4, "Cannot spawn command : %s", cmd);
  1898.  
  1899.     fs_give((void **)&command);
  1900.     if(result_file)
  1901.       fs_give((void **)&result_file);
  1902. }
  1903.  
  1904.  
  1905. /* ----------------------------------------------------------------------
  1906.    Execute the given mailcap test= cmd
  1907.  
  1908.   Args: cmd -- command to execute
  1909.   Returns exit status
  1910.   
  1911.   ----*/
  1912. int
  1913. exec_mailcap_test_cmd(cmd)
  1914.     char *cmd;
  1915. {
  1916.     return(system(cmd));
  1917. }
  1918.  
  1919.  
  1920.  
  1921. /*======================================================================
  1922.     print routines
  1923.    
  1924.     Functions having to do with printing on paper and forking of spoolers
  1925.  
  1926.     In general one calls open_printer() to start printing. One of
  1927.     the little print functions to send a line or string, and then
  1928.     call print_end() when complete. This takes care of forking off a spooler
  1929.     and piping the stuff down it. No handles or anything here because there's
  1930.     only one printer open at a time.
  1931.  
  1932.  ====*/
  1933.  
  1934.  
  1935.  
  1936. static char *trailer;  /* so both open and close_printer can see it */
  1937.  
  1938. /*----------------------------------------------------------------------
  1939.        Open the printer
  1940.  
  1941.   Args: desc -- Description of item to print. Should have one trailing blank.
  1942.  
  1943.   Return value: < 0 is a failure.
  1944.         0 a success.
  1945.  
  1946. This does most of the work of popen so we can save the standard output of the
  1947. command we execute and send it back to the user.
  1948.   ----*/
  1949. int
  1950. open_printer(desc)
  1951.     char     *desc;
  1952. {
  1953.     char command[201], prompt[200];
  1954.     int  cmd, rc, just_one;
  1955.     char *p, *init, *nick;
  1956.     char aname[100];
  1957.     char *printer;
  1958.     int     done = 0, i, lastprinter, cur_printer = 0;
  1959.     HelpType help;
  1960.     char   **list;
  1961.     static ESCKEY_S ekey[] = {
  1962.     {'y', 'y', "Y", "Yes"},
  1963.     {'n', 'n', "N", "No"},
  1964.     {ctrl('P'), 10, "^P", "Prev Printer"},
  1965.     {ctrl('N'), 11, "^N", "Next Printer"},
  1966.     {-2,   0,   NULL, NULL},
  1967.     {'c', 'c', "C", "CustomPrint"},
  1968.     {KEY_UP,    10, "", ""},
  1969.     {KEY_DOWN,  11, "", ""},
  1970.     {-1, 0, NULL, NULL}};
  1971. #define PREV_KEY   2
  1972. #define NEXT_KEY   3
  1973. #define CUSTOM_KEY 5
  1974. #define UP_KEY     6
  1975. #define DOWN_KEY   7
  1976.  
  1977.     trailer      = NULL;
  1978.     init         = NULL;
  1979.     nick         = NULL;
  1980.     command[200] = '\0';
  1981.  
  1982.     if(ps_global->VAR_PRINTER == NULL){
  1983.         q_status_message(SM_ORDER | SM_DING, 3, 5,
  1984.     "No printer has been chosen.  Use SETUP on main menu to make choice.");
  1985.     return(-1);
  1986.     }
  1987.  
  1988.     /* Is there just one print command available? */
  1989.     just_one = (ps_global->printer_category!=3&&ps_global->printer_category!=2)
  1990.            || (ps_global->printer_category == 2
  1991.            && !(ps_global->VAR_STANDARD_PRINTER
  1992.             && ps_global->VAR_STANDARD_PRINTER[0]
  1993.             && ps_global->VAR_STANDARD_PRINTER[1]))
  1994.            || (ps_global->printer_category == 3
  1995.            && !(ps_global->VAR_PERSONAL_PRINT_COMMAND
  1996.             && ps_global->VAR_PERSONAL_PRINT_COMMAND[0]
  1997.             && ps_global->VAR_PERSONAL_PRINT_COMMAND[1]));
  1998.  
  1999.     if(F_ON(F_CUSTOM_PRINT, ps_global))
  2000.       ekey[CUSTOM_KEY].ch = 'c'; /* turn this key on */
  2001.     else
  2002.       ekey[CUSTOM_KEY].ch = -2;  /* turn this key off */
  2003.  
  2004.     if(just_one){
  2005.     ekey[PREV_KEY].ch = -2;  /* turn these keys off */
  2006.     ekey[NEXT_KEY].ch = -2;
  2007.     ekey[UP_KEY].ch   = -2;
  2008.     ekey[DOWN_KEY].ch = -2;
  2009.     }
  2010.     else{
  2011.     ekey[PREV_KEY].ch = ctrl('P'); /* turn these keys on */
  2012.     ekey[NEXT_KEY].ch = ctrl('N');
  2013.     ekey[UP_KEY].ch   = KEY_UP;
  2014.     ekey[DOWN_KEY].ch = KEY_DOWN;
  2015.     /*
  2016.      * count how many printers in list and find the default in the list
  2017.      */
  2018.     if(ps_global->printer_category == 2)
  2019.       list = ps_global->VAR_STANDARD_PRINTER;
  2020.     else
  2021.       list = ps_global->VAR_PERSONAL_PRINT_COMMAND;
  2022.  
  2023.     for(i = 0; list[i]; i++)
  2024.       if(strcmp(ps_global->VAR_PRINTER, list[i]) == 0)
  2025.         cur_printer = i;
  2026.     
  2027.     lastprinter = i - 1;
  2028.     }
  2029.  
  2030.     help = NO_HELP;
  2031.     ps_global->mangled_footer = 1;
  2032.  
  2033.     while(!done){
  2034.     if(init)
  2035.       fs_give((void **)&init);
  2036.  
  2037.     if(trailer)
  2038.       fs_give((void **)&trailer);
  2039.  
  2040.     if(just_one)
  2041.       printer = ps_global->VAR_PRINTER;
  2042.     else
  2043.       printer = list[cur_printer];
  2044.  
  2045.     parse_printer(printer, &nick, &p, &init, &trailer, NULL, NULL);
  2046.     strncpy(command, p, 200);
  2047.     fs_give((void **)&p);
  2048.     sprintf(prompt, "Print %susing \"%s\" ? ", desc ? desc : "",
  2049.         *nick ? nick : command);
  2050.  
  2051.     fs_give((void **)&nick);
  2052.     
  2053.     cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global),
  2054.                  ekey, 'y', 'x', help, RB_NORM);
  2055.     
  2056.     switch(cmd){
  2057.       case 'y':
  2058.         q_status_message1(SM_ORDER, 0, 9,
  2059.         "Printing with command \"%s\"", command);
  2060.         done++;
  2061.         break;
  2062.  
  2063.       case 10:
  2064.         cur_printer = (cur_printer>0)
  2065.                 ? (cur_printer-1)
  2066.                 : lastprinter;
  2067.         break;
  2068.  
  2069.       case 11:
  2070.         cur_printer = (cur_printer<lastprinter)
  2071.                 ? (cur_printer+1)
  2072.                 : 0;
  2073.         break;
  2074.  
  2075.       case 'n':
  2076.       case 'x':
  2077.         done++;
  2078.         break;
  2079.  
  2080.       case 'c':
  2081.         done++;
  2082.         break;
  2083.  
  2084.       default:
  2085.         break;
  2086.     }
  2087.     }
  2088.  
  2089.     if(cmd == 'c'){
  2090.     if(init)
  2091.       fs_give((void **)&init);
  2092.  
  2093.     if(trailer)
  2094.       fs_give((void **)&trailer);
  2095.  
  2096.     sprintf(prompt, "Enter custom command : ");
  2097.     command[0] = '\0';
  2098.     rc = 1;
  2099.     while(rc){
  2100.         rc = optionally_enter(command, -FOOTER_ROWS(ps_global), 0,
  2101.         200, 1, 0, prompt, NULL, NO_HELP, 0);
  2102.         
  2103.         if(rc == 1){
  2104.         cmd = 'x';
  2105.         rc = 0;
  2106.         }
  2107.         else if(rc == 3)
  2108.           q_status_message(SM_ORDER, 0, 3, "No help available");
  2109.         else if(rc == 0){
  2110.         removing_trailing_white_space(command);
  2111.         removing_leading_white_space(command);
  2112.         q_status_message1(SM_ORDER, 0, 9,
  2113.             "Printing with command \"%s\"", command);
  2114.         }
  2115.     }
  2116.     }
  2117.  
  2118.     if(cmd == 'x' || cmd == 'n'){
  2119.     q_status_message(SM_ORDER, 0, 2, "Print cancelled");
  2120.     if(init)
  2121.       fs_give((void **)&init);
  2122.  
  2123.     if(trailer)
  2124.       fs_give((void **)&trailer);
  2125.  
  2126.     return(-1);
  2127.     }
  2128.  
  2129.     display_message('x');
  2130.  
  2131.     ps_global->print = (PRINT_S *)fs_get(sizeof(PRINT_S));
  2132.     memset(ps_global->print, 0, sizeof(PRINT_S));
  2133.  
  2134.     strcat(strcpy(aname, ANSI_PRINTER), "-no-formfeed");
  2135.     if(strucmp(command, ANSI_PRINTER) == 0
  2136.        || strucmp(command, aname) == 0){
  2137.         /*----------- Printer attached to ansi device ---------*/
  2138.         q_status_message(SM_ORDER, 0, 9,
  2139.         "Printing to attached desktop printer...");
  2140.         display_message('x');
  2141.     xonxoff_proc(1);            /* make sure XON/XOFF used */
  2142.     crlf_proc(1);                /* AND LF->CR xlation */
  2143.         fputs("\033[5i", stdout);
  2144.         ps_global->print->fp = stdout;
  2145.         if(strucmp(command, ANSI_PRINTER) == 0){
  2146.         /* put formfeed at the end of the trailer string */
  2147.         if(trailer){
  2148.         int len = strlen(trailer);
  2149.  
  2150.         fs_resize((void **)&trailer, len+2);
  2151.         trailer[len] = '\f';
  2152.         trailer[len+1] = '\0';
  2153.         }
  2154.         else
  2155.           trailer = cpystr("\f");
  2156.     }
  2157.     }
  2158.     else{
  2159.         /*----------- Print by forking off a UNIX command ------------*/
  2160.         dprint(4, (debugfile, "Printing using command \"%s\"\n", command));
  2161.     ps_global->print->result = temp_nam(NULL, "pine_prt");
  2162.     if(ps_global->print->pipe = open_system_pipe(command,
  2163.                      &ps_global->print->result, NULL,
  2164.                      PIPE_WRITE | PIPE_STDERR)){
  2165.         ps_global->print->fp = ps_global->print->pipe->ofilep;
  2166.     }
  2167.     else{
  2168.         fs_give((void *)&ps_global->print->result);
  2169.             q_status_message1(SM_ORDER | SM_DING, 3, 4,
  2170.                   "Error opening printer: %s",
  2171.                               error_description(errno));
  2172.             dprint(2, (debugfile, "Error popening printer \"%s\"\n",
  2173.                       error_description(errno)));
  2174.         if(init)
  2175.           fs_give((void **)&init);
  2176.  
  2177.         if(trailer)
  2178.           fs_give((void **)&trailer);
  2179.         
  2180.         return(-1);
  2181.         }
  2182.     }
  2183.  
  2184.     ps_global->print->err = 0;
  2185.     if(init){
  2186.     if(*init)
  2187.       fputs(init, ps_global->print->fp);
  2188.  
  2189.     fs_give((void **)&init);
  2190.     }
  2191.  
  2192.     return(0);
  2193. }
  2194.  
  2195.  
  2196.  
  2197. /*----------------------------------------------------------------------
  2198.      Close printer
  2199.   
  2200.   If we're piping to a spooler close down the pipe and wait for the process
  2201. to finish. If we're sending to an attached printer send the escape sequence.
  2202. Also let the user know the result of the print
  2203.  ----*/
  2204. void
  2205. close_printer()
  2206. {
  2207.     if(trailer){
  2208.     if(*trailer)
  2209.       fputs(trailer, ps_global->print->fp);
  2210.  
  2211.     fs_give((void **)&trailer);
  2212.     }
  2213.  
  2214.     if(ps_global->print->fp == stdout) {
  2215.         fputs("\033[4i", stdout);
  2216.         fflush(stdout);
  2217.     xonxoff_proc(0);            /* turn off XON/XOFF */
  2218.     crlf_proc(0);                /* turn off CF->LF xlantion */
  2219.     } else {
  2220.     (void) close_system_pipe(&ps_global->print->pipe);
  2221.     display_output_file(ps_global->print->result, "PRINT", NULL);
  2222.     fs_give((void *)&ps_global->print->result);
  2223.     }
  2224.  
  2225.     fs_give((void **)&ps_global->print);
  2226.  
  2227.     q_status_message(SM_ASYNC, 0, 3, "Print command completed");
  2228.     display_message('x');
  2229. }
  2230.  
  2231.  
  2232.  
  2233. /*----------------------------------------------------------------------
  2234.      Print a single character
  2235.  
  2236.   Args: c -- char to print
  2237.   Returns: 1 on success, 0 on ps_global->print->err
  2238.  ----*/
  2239. int
  2240. print_char(c)
  2241.     int c;
  2242. {
  2243.     if(!ps_global->print->err && putc(c, ps_global->print->fp) == EOF)
  2244.       ps_global->print->err = 1;
  2245.  
  2246.     return(!ps_global->print->err);
  2247. }
  2248.  
  2249.  
  2250.  
  2251. /*----------------------------------------------------------------------
  2252.      Send a line of text to the printer
  2253.  
  2254.   Args:  line -- Text to print
  2255.  
  2256.   ----*/
  2257.     
  2258. void
  2259. print_text(line)
  2260.     char *line;
  2261. {
  2262.     if(!ps_global->print->err && fputs(line, ps_global->print->fp) == EOF)
  2263.       ps_global->print->err = 1;
  2264. }
  2265.  
  2266.  
  2267.  
  2268. /*----------------------------------------------------------------------
  2269.       printf style formatting with one arg for printer
  2270.  
  2271.  Args: line -- The printf control string
  2272.        a1   -- The 1st argument for printf
  2273.  ----*/
  2274. void
  2275. print_text1(line, a1)
  2276.     char *line, *a1;
  2277. {
  2278.     if(!ps_global->print->err
  2279.        && fprintf(ps_global->print->fp, line, a1) < 0)
  2280.       ps_global->print->err = 1;
  2281. }
  2282.  
  2283.  
  2284.  
  2285. /*----------------------------------------------------------------------
  2286.       printf style formatting with one arg for printer
  2287.  
  2288.  Args: line -- The printf control string
  2289.        a1   -- The 1st argument for printf
  2290.        a2   -- The 2nd argument for printf
  2291.  ----*/
  2292. void
  2293. print_text2(line, a1, a2)
  2294.     char *line, *a1, *a2;
  2295. {
  2296.     if(!ps_global->print->err
  2297.        && fprintf(ps_global->print->fp, line, a1, a2) < 0)
  2298.       ps_global->print->err = 1;
  2299. }
  2300.  
  2301.  
  2302.  
  2303. /*----------------------------------------------------------------------
  2304.       printf style formatting with one arg for printer
  2305.  
  2306.  Args: line -- The printf control string
  2307.        a1   -- The 1st argument for printf
  2308.        a2   -- The 2nd argument for printf
  2309.        a3   -- The 3rd argument for printf
  2310.  ----*/
  2311. void
  2312. print_text3(line, a1, a2, a3)
  2313.     char *line, *a1, *a2, *a3;
  2314. {
  2315.     if(!ps_global->print->err
  2316.        && fprintf(ps_global->print->fp, line, a1, a2, a3) < 0)
  2317.       ps_global->print->err = 1;
  2318. }
  2319.  
  2320. #ifdef DEBUG
  2321. /*----------------------------------------------------------------------
  2322.      Initialize debugging - open the debug log file
  2323.  
  2324.   Args: none
  2325.  
  2326.  Result: opens the debug logfile for dprints
  2327.  
  2328.    Opens the file "~/.pine-debug1. Also maintains .pine-debug[2-4]
  2329.    by renaming them each time so the last 4 sessions are saved.
  2330.   ----*/
  2331. void
  2332. init_debug()
  2333. {
  2334.     char nbuf[5];
  2335.     char newfname[MAXPATH+1], filename[MAXPATH+1];
  2336.     int i;
  2337.  
  2338.     if(!debug)
  2339.       return;
  2340.  
  2341.     for(i = NUMDEBUGFILES - 1; i > 0; i--){
  2342.         build_path(filename, ps_global->home_dir, DEBUGFILE);
  2343.         strcpy(newfname, filename);
  2344.         sprintf(nbuf, "%d", i);
  2345.         strcat(filename, nbuf);
  2346.         sprintf(nbuf, "%d", i+1);
  2347.         strcat(newfname, nbuf);
  2348.         (void)rename_file(filename, newfname);
  2349.     }
  2350.  
  2351.     build_path(filename, ps_global->home_dir, DEBUGFILE);
  2352.     strcat(filename, "1");
  2353.  
  2354.     debugfile = fopen(filename, "w+");
  2355.     if(debugfile != NULL){
  2356.     time_t now = time((time_t *)0);
  2357.     if(debug > 7)
  2358.       setbuf(debugfile, NULL);
  2359.  
  2360.     if(NUMDEBUGFILES == 0){
  2361.         /*
  2362.          * If no debug files are asked for, make filename a tempfile
  2363.          * to be used for a record should pine later crash...
  2364.          */
  2365.         if(debug < 9)
  2366.           unlink(filename);
  2367.     }
  2368.  
  2369.     dprint(1, (debugfile, "Debug output of the Pine program (at debug"));
  2370.     dprint(1, (debugfile, " level %d).  Version %s\n%s\n",
  2371.           debug, pine_version, ctime(&now)));
  2372.     }
  2373. }
  2374.  
  2375.  
  2376. /*----------------------------------------------------------------------
  2377.      Try to save the debug file if we crash in a controlled way
  2378.  
  2379.   Args: dfile:  pointer to open debug file
  2380.  
  2381.  Result: tries to move the appropriate .pine-debugx file to .pine-crash
  2382.  
  2383.    Looks through the four .pine-debug files hunting for the one that is
  2384.    associated with this pine, and then renames it.
  2385.   ----*/
  2386. void
  2387. save_debug_on_crash(dfile)
  2388. FILE *dfile;
  2389. {
  2390.     char nbuf[5], crashfile[MAXPATH+1], filename[MAXPATH+1];
  2391.     int i;
  2392.     struct stat dbuf, tbuf;
  2393.     time_t now = time((time_t *)0);
  2394.  
  2395.     if(!(dfile && fstat(fileno(dfile), &dbuf) != 0))
  2396.       return;
  2397.  
  2398.     fprintf(dfile, "\nsave_debug_on_crash: Version %s: debug level %d\n",
  2399.     pine_version, debug);
  2400.     fprintf(dfile, "\n                   : %s\n", ctime(&now));
  2401.  
  2402.     build_path(crashfile, ps_global->home_dir, ".pine-crash");
  2403.  
  2404.     fprintf(dfile, "\nAttempting to save debug file to %s\n", crashfile);
  2405.     fprintf(stderr,
  2406.     "\n\n       Attempting to save debug file to %s\n\n", crashfile);
  2407.  
  2408.     /* Blat out last n keystrokes */
  2409.     fputs("========== Latest keystrokes ==========\n", dfile);
  2410.     while((i = key_recorder(0, 1)) != -1)
  2411.       fprintf(dfile, "\t%s\t(0x%04.4x)\n", pretty_command(i), i);
  2412.  
  2413.     /* look for existing debug file */
  2414.     for(i = 1; i <= NUMDEBUGFILES; i++){
  2415.     build_path(filename, ps_global->home_dir, DEBUGFILE);
  2416.     sprintf(nbuf, "%d", i);
  2417.     strcat(filename, nbuf);
  2418.     if(stat(filename, &tbuf) != 0)
  2419.       continue;
  2420.  
  2421.     /* This must be the current debug file */
  2422.     if(tbuf.st_dev == dbuf.st_dev && tbuf.st_ino == dbuf.st_ino){
  2423.         rename_file(filename, crashfile);
  2424.         break;
  2425.     }
  2426.     }
  2427.  
  2428.     /* if current debug file name not found, write it by hand */
  2429.     if(i > NUMDEBUGFILES){
  2430.     FILE *cfp;
  2431.     char  buf[1025];
  2432.  
  2433.     /*
  2434.      * Copy the debug temp file into the 
  2435.      */
  2436.     if(cfp = fopen(crashfile, "w")){
  2437.         buf[1024] = '\0';
  2438.         fseek(dfile, 0L, 0);
  2439.         while(fgets(buf, 1025, dfile) && fputs(buf, cfp) != EOF)
  2440.           ;
  2441.  
  2442.         fclose(cfp);
  2443.     }
  2444.     }
  2445.  
  2446.     fclose(dfile);
  2447. }
  2448.  
  2449.  
  2450. #define CHECK_EVERY_N_TIMES 100
  2451. #define MAX_DEBUG_FILE_SIZE 200000L
  2452. /*
  2453.  * This is just to catch runaway Pines that are looping spewing out
  2454.  * debugging (and filling up a file system).  The stop doesn't have to be
  2455.  * at all precise, just soon enough to hopefully prevent filling the
  2456.  * file system.  If the debugging level is high (9 for now), then we're
  2457.  * presumably looking for some problem, so don't truncate.
  2458.  */
  2459. int
  2460. do_debug(debug_fp)
  2461. FILE *debug_fp;
  2462. {
  2463.     static int counter = CHECK_EVERY_N_TIMES;
  2464.     static int ok = 1;
  2465.     long filesize;
  2466.  
  2467.     if(debug == DEFAULT_DEBUG && ok && --counter <= 0){
  2468.     if((filesize = fp_file_size(debug_fp)) != -1L)
  2469.       ok = (unsigned long)filesize < (unsigned long)MAX_DEBUG_FILE_SIZE;
  2470.  
  2471.     counter = CHECK_EVERY_N_TIMES;
  2472.     if(!ok){
  2473.         fprintf(debug_fp, "\n\n --- No more debugging ---\n");
  2474.         fprintf(debug_fp,
  2475.         "     (debug file growing too large - over %ld bytes)\n\n",
  2476.         MAX_DEBUG_FILE_SIZE);
  2477.         fflush(debug_fp);
  2478.     }
  2479.     }
  2480.     return(ok);
  2481. }
  2482. #endif /* DEBUG */
  2483.  
  2484.  
  2485.